home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Libraries / SAT 2.3.7 / Demos / Demo ƒ / Collision ⁄⁄⁄ demo ƒ / Collision ⁄⁄⁄.p < prev    next >
Encoding:
Text File  |  1995-07-15  |  37.0 KB  |  1,308 lines  |  [TEXT/PJMM]

  1. {Collision ///}
  2. {}
  3. {This demo demonstrates some alternative ways to use SAT:}
  4. {}
  5. {• The animation is called from the standard event loop (via TransSkel). This slows things down}
  6. {quite a bit (since all other processes are allowed to run), but makes the application background-}
  7. {friendly.}
  8. {• It runs in an ordinary, moveable window. We can, with some effort, do this while still}
  9. {using the fast mode (in which case we would have to restrict window dragging so it stays}
  10. {within the main screen, modify certain global (gSAT.ox ad gSAT.oy), and also limit the}
  11. {horizontal alignment of the window like HyperCard does), but in this demo we just use the}
  12. {slow (safe) mode. }
  13. {• We create sprites from QuickDraw calls instead of cicns.}
  14. {• We use the mask regions of the sprites for better collision detection.}
  15. {• We use a pattern as backkground instead of PICTs.}
  16. {• All the code is in one unit. This might make it less structured, less encapsulated, but I wanted}
  17. {to show you that you don't have to do things exactly the way I do in the other demos.}
  18. {• Using a modified sprite record.}
  19. {• Fixed-point positions}
  20. {}
  21. {However, some variations remain that aren't demonstrated even here:}
  22. {• Calculating the positions of the sprites with the system clock (TickCount or Time Manager) instead of}
  23. {moving them each frame. That approach has some advantages (i.e. constant speed on objects), can easily}
  24. {be used with SAT, but is not as simple.}
  25.  
  26.  
  27. program CollisionIII;
  28.  
  29.     uses
  30. {$ifc UNDEFINED THINK_PASCAL}
  31.         Types, QuickDraw, Events, Windows, Dialogs, Fonts, DiskInit, TextEdit, Traps, Memory, SegLoad,{}
  32.         Scrap, ToolUtils, OSUtils, Menus, Resources, StandardFile, GestaltEqu, Files, Errors, 
  33. {$elsec}
  34.         InterfacesUI,  {To give Think Pascal some UPI}
  35. {$endc}
  36.         TransSkel, SAT;
  37.  
  38. {A modified sprite record. This should usually go in some globals unit}
  39. {in your project.}
  40. type
  41.         C3SpritePtr = ^C3Sprite;
  42.         C3Sprite = record
  43. { Variables that you should change as appropriate }
  44.                 kind: Integer; { Used for identification. >0: friend. <0 foe }
  45.                 position: Point;
  46.                 hotRect, hotRect2: Rect; { Tells how large the sprite is; hotRect is centered around origo }
  47.                                         {hotRect is set by you. hotRect2 is offset to the current position.}
  48.                 face: FacePtr; { Pointer to the Face (appearance) to be used. }
  49.                 task: ProcPtr; { Callback-routine, called once per frame. If task=nil, the sprite is removed. }
  50.                 hitTask: ProcPtr; { Callback in collisions. }
  51.                 destructTask: ProcPtr; { Called when a sprite is disposed. (Usually nil.) }
  52.                 clip: RgnHandle;
  53. { SAT variables that you shouldn't change: }
  54.                 oldpos: Point;                {The 'task' routine is not allowed to change this! }
  55.                 next, prev: SpritePtr;    {You may change them in your own sorting routine, but be careful if you do.}
  56.                 r, oldr: Rect;                {Rectangle telling where to draw. Avoid messing with it.}
  57.                 oldFace: FacePtr;            {Used by RunSAT2}
  58.                 dirty: Boolean;            {Used by RunSAT2}
  59. {Variables for internal use by the sprites. I have edited them, to add fixed-point postions!}
  60. {Since we have edited the record, we must SetSpriteSize immediately}
  61. {after initializing (before any sprites are created)!}
  62.                 layer: integer; {For layer-sorting. When not used for that, use freely.}
  63.                 speed: Point; { Can be used for speed, but not necessarily. }
  64.                 mode: integer; { Usually used for different modes and/or to determine what image to show next. }
  65.                 fixedPos: Point; {Position * 16}
  66.             end;
  67.  
  68.     const
  69.         newgameItem = 1;
  70.         clearHighItem = 4;
  71.  
  72.         aboutAlrt = 128;
  73.         fileMenuRes = 128;
  74.         shapeMenuRes = 129;
  75.         theWindRes = 128;
  76.  
  77.         kGameTime = 3600; {60 sekunder}
  78.         kExtraTime = 180; {3 sekunder}
  79.         kLevelBonus = 25;
  80.  
  81.     type
  82.         SettingsRec = record
  83.                 high: Longint;
  84.                 player: string[5];
  85.             end;
  86.         SettingsPtr = ^SettingsRec;
  87.         SettingsHnd = ^SettingsPtr;
  88.     var
  89.         settings: SettingsHnd;
  90.         fileMenu, shapeMenu: MenuHandle;
  91.         gameRunning: Boolean;
  92.         gameStartTime, lastSetStartTime: Longint;
  93.         setCount: integer;
  94.         gMode: integer;
  95.         scoreFace, highFace, lastface: FacePtr;
  96.         myFace, welcomeFace: FacePtr;
  97.         score: Longint;
  98.         bgPat: SATPatHandle;
  99.  
  100.         scaledFace: array[0..31] of FacePtr;
  101.  
  102.     procedure Barf;
  103.     begin
  104.         SATReportStr('Something went wrong. Sorry.');
  105.         halt;
  106.     end;
  107.  
  108. {Ljud:}
  109.  
  110. {Konstruera en snd-resurs artificiellt}
  111. {Rutinen bygger en handle med reserverad plats för ljudet, som sedan värdrutinen kan skapa.}
  112.     function CreateSnd (size: longint; var sndH: handle; var dataPek: Ptr): Boolean;
  113.         type
  114.             mySndRec = packed record
  115.                     format: integer;
  116.                     numsynth: integer; {must be 0}
  117. {synth}
  118.                     synthid: integer;{5}
  119.                     synthinit: longint;{0}
  120.  
  121.                     numcom: integer; {must be 1}
  122. {command}
  123.                     command: integer;{ $8051}
  124.                     param1: integer; {0}
  125.                     param2: longint; { $14}
  126. {sound header}
  127.                     dataptr: Ptr;
  128.                     datasize: longint;
  129.                     samplerate: longint; {22kHz = $56ee8ba3}
  130.                     loopstart: Ptr;
  131.                     loopend: Ptr;
  132.                     encoding: Byte;{0}
  133.                     basenote: Byte; { $3c}
  134. {data}
  135.                     ljud: packed array[0..0] of Byte;
  136.                 end;
  137.             msrp = ^mySndRec;
  138.             msrh = ^msrp;
  139.         var
  140.             h: msrh;
  141.     begin
  142.         h := msrh(NewHandle(sizeof(mySndRec) + size));
  143.         if h = nil then
  144.             CreateSnd := false
  145.         else
  146.             begin
  147.                 HLock(Handle(h)); {Fixar detta buggen med att ljuden ändras?}
  148.                 with h^^ do
  149.                     begin
  150.                         format := 1;
  151.                         numsynth := 1;
  152.                         synthid := 5;
  153.                         synthinit := 0;
  154.                         numcom := 1;
  155.                         command := $8051;
  156.                         param1 := 0;
  157.                         param2 := $14;
  158.                         dataptr := @ljud[0];
  159.                         datasize := size;
  160.                         samplerate := $56ee8ba3; {div 2 - fast varför köra 11kHz när man synthar?!}
  161.                         loopstart := dataptr;
  162.                         loopend := dataptr; {?}
  163.                         encoding := 0;
  164.                         basenote := $3c;
  165.                         dataPek := dataptr; {Utdata}
  166.                     end; {with}
  167.                 SndH := handle(h);{utdata}
  168.                 CreateSnd := true;
  169.             end; {if nil else}
  170.     end;{CreateSnd}
  171.  
  172.     var
  173.         pushH, bippH, baeH: Handle;
  174.  
  175. {Fixa några bra subrutiner för ljudsyntning?!}
  176. {- Eko}
  177. {- Lågpass och högpass}
  178. {- Sampla upp eller ner?}
  179. {- Frekvensvariation?}
  180. {- Fade in, fade out (mm envelope)}
  181. {Drömmen är förstås FFT, så man kan göra riktigt vass bandspärr, frekvensskift mm.}
  182. {Apropå: kan man inte göra bra ljudkompression med FFT?}
  183.  
  184. {$PUSH}
  185. {$R-}
  186.  
  187. {Rutinen som skall bygga de syntetiska ljud vi önskar!}
  188.     procedure Synth;
  189.         type
  190.             ArtRec = record
  191.                     arr: packed array[0..10000] of Byte;
  192.                 end;
  193.             ArtPtr = ^ArtRec;
  194.         var
  195.             tmpPtr: ArtPtr;
  196.             i: integer;
  197.         const
  198.             pushSize = 3479;
  199.             bippSize = 2959;
  200.             baeSize = 20000;
  201.     begin
  202.         if not CreateSnd(pushSize + 1, pushH, Ptr(tmpptr)) then
  203.             CheckNoMem(nil); {EmergencyExit}
  204.         for i := 0 to pushSize do
  205.             tmpptr^.arr[i] := band(char(random), 127) * (pushSize - i) div pushSize + 128;
  206.         for i := 0 to pushSize - 3 do
  207.             tmpptr^.arr[i] := (tmpptr^.arr[i] + tmpptr^.arr[i + 1] + tmpptr^.arr[i + 2] + tmpptr^.arr[i + 3]) div 4;
  208.         for i := 0 to 64 do
  209.             begin
  210. {tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;}
  211.                 tmpptr^.arr[pushSize - i] := tmpptr^.arr[pushSize - i] * i div 64;
  212.             end;
  213.  
  214.         if not CreateSnd(bippSize + 1, bippH, Ptr(tmpptr)) then
  215.             CheckNoMem(nil); {EmergencyExit}
  216.         for i := 0 to bippSize do
  217.             tmpptr^.arr[i] := i mod (i div 171 + 1) mod 127 + 128; {mjiioo}
  218. {tmpptr^.arr[i] := i mod (i div 171 + 1) + 128; {mjiioo}
  219. {tmpptr^.arr[i] := i mod (i div 17 + 1) + 128; {maipp}
  220. {tmpptr^.arr[i] := band(i, 63) + 128;}
  221.         for i := 0 to 64 do
  222.             begin
  223.                 tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;
  224.                 tmpptr^.arr[bippSize - i] := tmpptr^.arr[bippSize - i] * i div 64;
  225.             end;
  226.  
  227.         if not CreateSnd(baeSize + 1, baeH, Ptr(tmpptr)) then
  228.             CheckNoMem(nil); {EmergencyExit}
  229.         for i := 0 to baeSize do
  230.             tmpptr^.arr[i] := (i div 5) mod (i div 1571 + 1) mod 127 + 128; {mjiioo}
  231. {tmpptr^.arr[i] := i mod (i div 171 + 1) + 128; {mjiioo}
  232. {tmpptr^.arr[i] := i mod (i div 17 + 1) + 128; {maipp}
  233. {tmpptr^.arr[i] := band(i, 63) + 128;}
  234.         for i := 0 to baeSize - 3 do
  235.             tmpptr^.arr[i] := (tmpptr^.arr[i] + tmpptr^.arr[i + 1] + tmpptr^.arr[i + 2] + tmpptr^.arr[i + 3]) div 4;
  236.         for i := 0 to 64 do
  237.             begin
  238.                 tmpptr^.arr[i] := tmpptr^.arr[i] * i div 64;
  239.                 tmpptr^.arr[baeSize - i] := tmpptr^.arr[baeSize - i] * i div 64;
  240.             end;
  241.     end;
  242. {$POP}
  243.  
  244.     procedure DoAbout;
  245.     begin
  246.         if 1 = Alert(aboutAlrt, nil) then
  247.             ;
  248.     end;
  249.  
  250. {Two handly routines from my dialog utilities unit.}
  251.     procedure SetTextDItem (theDialog: DialogPtr; itemNo: integer; theString: Str255);
  252.         var
  253.             kind: integer;
  254.             item: ControlHandle;
  255.             box: Rect;
  256.     begin
  257.         GetDialogItem(theDialog, itemNo, kind, Handle(item), box);
  258. {Check kind}
  259.         kind := BitAnd(kind, 127);
  260.         case kind of
  261.             8, 16: {statText, editText}
  262.                 SetDialogItemText(handle(item), theString);
  263.             0, 1, 2, 4, 5, 6: {button, checkbox, radio - men vad är 4?}
  264.                 SetControlTitle(item, theString);
  265.             otherwise {Övriga har ingen text man kan sätta}
  266.                 SysBeep(1);
  267.         end;{case}
  268.     end;
  269.     function GetTextDItem (theDialog: DialogPtr; itemNo: integer): Str255;
  270.         var
  271.             kind: integer;
  272.             item: ControlHandle;
  273.             box: Rect;
  274.             tmpStr: Str255;
  275.     begin
  276.         GetDialogItem(theDialog, itemNo, kind, Handle(item), box);
  277. {Check kind}
  278.         kind := BitAnd(kind, 127);
  279.         tmpStr := '';
  280.         case kind of
  281.             8, 16: {statText, editText}
  282.                 GetDialogItemText(handle(item), tmpStr);
  283.             0, 1, 2, 4, 5, 6: {button, checkbox, radio…?}
  284.                 GetControlTitle(item, tmpStr);
  285.             otherwise {Övriga har ingen text man kan sätta}
  286.                 SysBeep(1);
  287.         end;{case}
  288.         GetTextDItem := tmpStr;
  289.     end;
  290.     function MyNumToString (l: longint): Str255;
  291.         var
  292.             tmpStr: Str255;
  293.     begin
  294.         NumToString(l, tmpStr);
  295.         MyNumToString := tmpStr;
  296.     end;
  297.  
  298. {Make the new high score dialog}
  299.     procedure AskHigh;
  300.         const
  301.             highDlogID = 129;
  302.         var
  303.             dialog: DialogPtr;
  304.             oldPort: GrafPtr;
  305.             itemHit: integer;
  306.             str: str255;
  307.     begin
  308.         GetPort(oldPort);
  309.         dialog := GetNewDialog(highDlogID, nil, WindowPtr(-1));
  310.         ShowWindow(dialog);
  311.         SelectWindow(dialog);
  312.         SetPort(dialog);
  313.  
  314.         SetTextDItem(dialog, 3, settings^^.player);
  315.         SelectDialogItemText(dialog, 3, 0, 32767);
  316.         itemHit := -1;
  317.         while (itemHit <> 1) and (itemHit <> 2) do { 1=ok, 2=cancel }
  318.             ModalDialog(nil, itemHit);
  319.         if itemHit = 1 then
  320.             begin
  321.                 str := GetTextDItem(dialog, 3);
  322.                 if length(str) > 5 then
  323.                     str[0] := char(5); {snabbaste sättet att korta den!}
  324.                 settings^^.player := str;
  325.                 settings^^.high := score;
  326.             end;
  327.         DisposeDialog(dialog);
  328.         SetPort(oldPort);
  329.     end;
  330.  
  331. {Reuseable sprite movement routine, called from all sprite handling routines. Some sprites use this}
  332. {as handling routine.}
  333.     procedure SATBounce (me: SpritePtr);
  334.     begin
  335.         me^.position.h := me^.position.h + me^.speed.h;
  336.         me^.position.v := me^.position.v + me^.speed.v;
  337.         if me^.position.h < 0 then
  338.             me^.speed.h := abs(me^.speed.h);
  339.         if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  340.             me^.speed.h := -abs(me^.speed.h);
  341.         if me^.position.v < 0 then
  342.             me^.speed.v := abs(me^.speed.v);
  343.         if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  344.             me^.speed.v := -abs(me^.speed.v);
  345.     end;
  346.  
  347. {The same but using fixed-point position, as in HandlePlayer}
  348.     procedure SATFixedBounce (me: C3SpritePtr);
  349.     begin
  350.         me^.fixedPos.h := me^.fixedPos.h + me^.speed.h;
  351.         me^.fixedPos.v := me^.fixedPos.v + me^.speed.v;
  352.  
  353.         me^.position.h := BSR(me^.fixedPos.h, 4); {Shift left 4 steps, i.e. div 16}
  354.         me^.position.v := BSR(me^.fixedPos.v, 4);
  355.  
  356. {Since BSR isn't aritmetic shift, a negative fixedPos will unfortunately result in}
  357. {a very large positive position. This must be accounted for when checking borders}
  358. {- or we could use div, but that is slower.}
  359.  
  360.         if me^.fixedPos.h < 0 then
  361.             begin
  362.                 me^.speed.h := abs(me^.speed.h);
  363.                 me^.position.h := 0;
  364.             end
  365.         else if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  366.             me^.speed.h := -abs(me^.speed.h);
  367.         if me^.fixedPos.v < 0 then
  368.             begin
  369.                 me^.speed.v := abs(me^.speed.v);
  370.                 me^.position.v := 0;
  371.             end
  372.         else if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  373.             me^.speed.v := -abs(me^.speed.v);
  374.     end;
  375.  
  376.  
  377.     procedure HandleTheSprite (me: C3SpritePtr);
  378.     begin
  379.         if me^.speed.h = 0 then
  380.             me^.speed.h := SATRand(32) - SATRand(32);
  381.         if me^.speed.v = 0 then
  382.             me^.speed.v := SATRand(32) - SATRand(32);
  383.         if me^.face = nil then
  384.             begin
  385.                 me^.face := myFace;
  386.                 if me^.face <> nil then
  387.                     me^.hotRect := me^.face^.iconMask.bounds;
  388.             end;
  389.         SATFixedBounce(me);
  390.     end;
  391.  
  392.     procedure RedrawScoreFace;
  393.     begin
  394.         SATSetPortFace(scoreFace);
  395.         EraseRect(scoreFace^.iconMask.bounds);
  396.         MoveTo(2, 14);
  397.         ForeColor(blackColor);
  398.         DrawString('Score:');
  399.         SATDrawLong(score);
  400.         ForeColor(whiteColor);
  401.         MoveTo(0, 12);
  402.         DrawString('Score:');
  403.         SATDrawLong(score);
  404.         ForeColor(blackColor);
  405.         SATSetPortScreen;
  406.         SATSetPortMask(scoreFace);
  407.         EraseRect(scoreFace^.iconMask.bounds);
  408.         MoveTo(0, 12);
  409.         DrawString('Score:');
  410.         SATDrawLong(score);
  411.         MoveTo(2, 14);
  412.         DrawString('Score:');
  413.         SATDrawLong(score);
  414.         SATSetPortScreen;
  415.         SATChangedFace(scoreFace);
  416.     end;
  417.  
  418.     procedure RedrawHighFace;
  419.         var
  420.             str: Str255;
  421.     begin
  422.         str := stringof('High score:', MyNumToString(settings^^.high), ' by ', settings^^.player);
  423.  
  424.         SATSetPortFace(highFace);
  425.         EraseRect(highFace^.iconMask.bounds);
  426.         MoveTo(2, 14);
  427.         ForeColor(blackColor);
  428.         DrawString(str);
  429.         ForeColor(whiteColor);
  430.         MoveTo(0, 12);
  431.         DrawString(str);
  432.         ForeColor(blackColor);
  433.         SATSetPortScreen;
  434.         SATSetPortMask(highFace);
  435.         EraseRect(highFace^.iconMask.bounds);
  436.         MoveTo(0, 12);
  437.         DrawString(str);
  438.         MoveTo(2, 14);
  439.         DrawString(str);
  440.         SATSetPortScreen;
  441.         SATChangedFace(highFace);
  442.     end;
  443.  
  444.     procedure RedrawLastFace;
  445.     begin
  446.         SATSetPortFace(lastface);
  447.         EraseRect(lastface^.iconMask.bounds);
  448.         MoveTo(2, 14);
  449.         DrawString('Last score:');
  450.         SATDrawLong(score);
  451.         ForeColor(whiteColor);
  452.         MoveTo(0, 12);
  453.         DrawString('Last score:');
  454.         SATDrawLong(score);
  455.         ForeColor(blackColor);
  456.         SATSetPortScreen;
  457.         SATSetPortMask(lastface);
  458.         EraseRect(lastface^.iconMask.bounds);
  459.         MoveTo(0, 12);
  460.         DrawString('Last score:');
  461.         SATDrawLong(score);
  462.         MoveTo(2, 14);
  463.         DrawString('Last score:');
  464.         SATDrawLong(score);
  465.         SATSetPortScreen;
  466.         SATChangedFace(lastface);
  467.     end;
  468.  
  469.     var
  470.         playerFace: array[0..15] of FacePtr;
  471.         playerSpeed: array[0..15] of Point;
  472.  
  473.  
  474.  
  475.  
  476.  
  477. {Redraw all player faces. This is separated from InitPlayerFaces since it must be called on}
  478. {depth changes.}
  479.     procedure ReDrawPlayerFaces;
  480.         const
  481.             totalAngle = 240;
  482.         var
  483.             i: integer;
  484.             r, r1, r2, ri: Rect;
  485.             reg1, reg2: RgnHandle;
  486.             pol: PolyHandle;
  487.     begin
  488.         SetRect(r, 0, 0, 40, 40); {Total face size}
  489.         SetRect(r1, 0, 0, 38, 38); {Colored part}
  490.         SetRect(ri, 9, 9, 29, 29); {Colored part, inner circle}
  491.         SetRect(r2, 2, 2, 40, 40); {Shadow}
  492.         for i := 0 to 15 do
  493.             begin
  494.                 reg1 := NewRgn;
  495.                 reg2 := NewRgn;
  496.  
  497. {Generate shape}
  498.                 SATSetPortMask(playerFace[i]);
  499.                 PaintArc(r1, i * 360 div 16 - (360 - totalAngle) div 2, totalAngle);
  500.                 EraseArc(ri, i * 360 div 16 - (360 - totalAngle) div 2, totalAngle); {360-graders-skala}
  501. {$IFC GENERATINGPOWERPC }
  502.                 if noErr <> BitMapToRegion(reg1, playerFace[i]^.iconMask) then{}
  503.                     ;
  504. {$ELSEC}
  505.                 if noErr <> BitMapToRegionGlue(reg1, playerFace[i]^.iconMask) then{}
  506.                     ;
  507. {$ENDC}
  508.                 CopyRgn(reg1, reg2);
  509.                 OffsetRgn(reg2, 2, 2);
  510.  
  511. {Draw face}
  512.                 SATSetPortFace(playerFace[i]);
  513.                 EraseRect(playerFace[i]^.iconMask.bounds);
  514.                 ForeColor(blackColor);
  515.                 PaintRgn(reg2); {black "Shadow"}
  516.                 ForeColor(cyanColor);
  517.                 if gSAT.initDepth > 1 then
  518.                     PaintRgn(reg1) {If we run in color, fill it completely with cyan}
  519.                 else
  520. {$IFC UNDEFINED THINK_PASCAL}
  521.                     FillRgn(reg1, qd.ltGray); {If we run in b/w, a gray pattern looks nicer}
  522. {$ELSEC}
  523.                 FillRgn(reg1, ltGray); {If we run in b/w, a gray pattern looks nicer}
  524. {$ENDC}
  525.                 ForeColor(blueColor);
  526.                 FrameRgn(reg1);
  527.                 ForeColor(blackColor);
  528. {Draw mask}
  529.                 SATSetPortMask(playerFace[i]);
  530.                 EraseRect(playerFace[i]^.iconMask.bounds);
  531.                 PaintRgn(reg1);
  532.                 PaintRgn(reg2);
  533.                 SATSetPortScreen;
  534.                 SATChangedFace(playerFace[i]);
  535.  
  536.                 DisposeRgn(reg1);
  537.                 DisposeRgn(reg2);
  538.             end;
  539.     end;
  540.  
  541. {Create all player faces.}
  542.     procedure InitPlayerFaces;
  543.         var
  544.             i: integer;
  545.             r: Rect;
  546.     begin
  547. {We use crude approximations to the sine/cosine functions we really want.}
  548. {A real game might init the table by using sine and cosine for real, but I don't}
  549. {want to make this harder to read than it already is. A real game would also}
  550. {use more than 16 directions, say 32 or even 64.}
  551.  
  552.         SetPt(playerSpeed[6], 0, -6);
  553.         SetPt(playerSpeed[7], 2, -5);
  554.         SetPt(playerSpeed[8], 4, -4);
  555.         SetPt(playerSpeed[9], 5, -2);
  556.         SetPt(playerSpeed[10], 6, 0);
  557.         SetPt(playerSpeed[11], 5, 2);
  558.         SetPt(playerSpeed[12], 4, 4);
  559.         SetPt(playerSpeed[13], 2, 5);
  560.         SetPt(playerSpeed[14], 0, 6);
  561.         SetPt(playerSpeed[15], -2, 5);
  562.         SetPt(playerSpeed[0], -4, 4);
  563.         SetPt(playerSpeed[1], -5, 2);
  564.         SetPt(playerSpeed[2], -6, 0);
  565.         SetPt(playerSpeed[3], -5, -2);
  566.         SetPt(playerSpeed[4], -4, -4);
  567.         SetPt(playerSpeed[5], -2, -5);
  568.  
  569.         SetPt(playerSpeed[6], 0, -32);
  570.         SetPt(playerSpeed[7], 14, -28);
  571.         SetPt(playerSpeed[8], 22, -22);
  572.         SetPt(playerSpeed[9], 28, -14);
  573.         SetPt(playerSpeed[10], 32, 0);
  574.         SetPt(playerSpeed[11], 28, 14);
  575.         SetPt(playerSpeed[12], 22, 22);
  576.         SetPt(playerSpeed[13], 14, 28);
  577.         SetPt(playerSpeed[14], 0, 32);
  578.         SetPt(playerSpeed[15], -14, 28);
  579.         SetPt(playerSpeed[0], -22, 22);
  580.         SetPt(playerSpeed[1], -28, 14);
  581.         SetPt(playerSpeed[2], -32, 0);
  582.         SetPt(playerSpeed[3], -28, -14);
  583.         SetPt(playerSpeed[4], -22, -22);
  584.         SetPt(playerSpeed[5], -14, -28);
  585.  
  586.         SetRect(r, 0, 0, 40, 40); {Total face size}
  587.         for i := 0 to 15 do
  588.             begin
  589.                 playerFace[i] := SATNewFace(r);
  590.                 SATChangedFace(playerFace[i]);
  591.             end;
  592.         RedrawPlayerFaces;
  593.     end;
  594.  
  595.     procedure HandlePlayer (me: C3SpritePtr);
  596.     begin
  597.         me^.mode := gMode;
  598.         me^.face := playerFace[me^.mode];
  599.  
  600.         me^.fixedPos.h := me^.fixedPos.h + playerSpeed[me^.mode].h;
  601.         me^.fixedPos.v := me^.fixedPos.v + playerSpeed[me^.mode].v;
  602.  
  603.         me^.position.h := BSR(me^.fixedPos.h, 4); {Shift left 4 steps, i.e. div 16}
  604.         me^.position.v := BSR(me^.fixedPos.v, 4);
  605.  
  606.         if me^.fixedPos.h < 0 then
  607.             begin
  608.                 me^.position.h := 0;
  609.                 me^.fixedPos.h := 0;
  610. {gMode := BitAnd(BitAnd(4 - gMode, 15) + 4, 15);}
  611.             end;
  612.         if me^.position.h > gSAT.offSizeH - me^.hotRect.right then
  613.             begin
  614.                 me^.position.h := gSAT.offSizeH - me^.hotRect.right;
  615.                 me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  616. {gMode := BitAnd(BitAnd(4 - gMode, 15) + 4, 15);}
  617.             end;
  618.         if me^.fixedPos.v < 0 then
  619.             begin
  620.                 me^.position.v := 0;
  621.                 me^.fixedPos.v := 0;
  622. {gMode := BitAnd(-gMode, 15);}
  623.             end;
  624.         if me^.position.v > gSAT.offSizeV - me^.hotRect.bottom then
  625.             begin
  626.                 me^.position.v := gSAT.offSizeV - me^.hotRect.bottom;
  627.                 me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  628. {gMode := BitAnd(-gMode, 15);}
  629.             end;
  630.     end;
  631.  
  632. {Get a vector from center to center of two sprites}
  633.     function Vector (s1, s2: SpritePtr): Point;
  634.     begin
  635.         Vector.h := s1^.position.h + s1^.face^.iconMask.bounds.right div 2 - s2^.position.h - s2^.face^.iconMask.bounds.right div 2;
  636.         Vector.v := s1^.position.v + s1^.face^.iconMask.bounds.right div 2 - s2^.position.v - s2^.face^.iconMask.bounds.right div 2;
  637.     end;
  638.  
  639. {Squared distance between centers of two sprites}
  640.     function Dist2 (s1, s2: SpritePtr): Longint;
  641.         var
  642.             v: Point;
  643.     begin
  644.         v := Vector(s1, s2);
  645.         Dist2 := v.h * v.h + v.v * v.v;
  646.     end;
  647.  
  648.     procedure CreatePill;
  649.     forward;
  650.  
  651.  
  652. {***Check for hits based on regions - reuseable procedure!***}
  653.     function RegionHitTest (s1, s2: SpritePtr): Boolean;
  654.         var
  655.             r1, r2: RgnHandle;
  656.     begin
  657. {We know that out hotRects coincide. However, that doesn't mean that we must take it as a}
  658. {collision! Rather, we can do more processing here to decide whether or not it was a collision.}
  659. {In this case, we copy the mask regions of each sprite, offset them to the proper positions,}
  660. {and check if they, too, overlap!}
  661. {}
  662. {Do you think we are doing double work, both dealing with hotRects and the regions? If you do,}
  663. {let me explain some more. The idea is that SAT checks the hotRects for you, which takes away}
  664. {next to all false hits. Checking hotRects is *fast*, so that's what we can afford to do all-to-all}
  665. {(or all-to-near, depending on the chosen search mode). Once a *possible* collision is detected,}
  666. {we can spend some time analyzing it further!}
  667.  
  668. {First of all, let's do some error checking. We could also have done this when loading the faces.}
  669. {Most programs won't have to bother whether or not the regions have been generated}
  670. {successfully, but when using them this way, they must exist or we may get a crash.}
  671.         if (s1^.face^.maskRgn = nil) or (s2^.face^.maskRgn = nil) then
  672.             begin
  673.                 SATReportStr('Error: No mask region!');
  674.                 exit(RegionHitTest);
  675.             end;
  676.  
  677. {Make copies of the mask regions and offset them to the proper places.}
  678.         r1 := NewRgn;
  679.         r2 := NewRgn;
  680.         CopyRgn(s1^.face^.maskRgn, r1);
  681.         CopyRgn(s2^.face^.maskRgn, r2);
  682.         OffsetRgn(r1, s1^.position.h, s1^.position.v);
  683.         OffsetRgn(r2, s2^.position.h, s2^.position.v);
  684.  
  685.         SectRgn(r1, r2, r1);                    {Is there any overlap?}
  686.  
  687. {If empty, no collision, otherwise, handle the collision!}
  688.         RegionHitTest := not EmptyRgn(r1);
  689.  
  690.         DisposeRgn(r1);
  691.         DisposeRgn(r2);
  692.     end;
  693.  
  694. {Collision handling for the player sprite}
  695.     procedure HitPlayer (me, him: SpritePtr);
  696.         var
  697.             v: Point;
  698.     begin
  699.         if RegionHitTest(me, him) then {Do the sprites *really* overlap?}
  700.             begin
  701.  
  702.                 if Dist2(me, him) > 60 then
  703.                     begin
  704. {Hit too far out, so let's call it the outside. Bounce away him.}
  705. {We could make more efforts here for a good bounce.}
  706.                         him^.position.h := him^.position.h + me^.speed.h;
  707.                         him^.speed.h := -him^.speed.h + me^.speed.h;
  708.                         him^.position.v := him^.position.v + me^.speed.v;
  709.                         him^.speed.v := -him^.speed.v + me^.speed.v;
  710. {Finally, make sure the other is moving *away* from us!}
  711. {And when we're at it, why not move it just a little, too?}
  712. {Yuck, this is ugly! Yup, careless programming. Hack, hack!}
  713.                         v := Vector(me, him);
  714.                         if v.h > 0 then
  715.                             begin
  716.                                 if him^.speed.h > 0 then
  717.                                     him^.speed.h := -him^.speed.h;
  718.                                 him^.position.h := him^.position.h - 1;
  719.                             end
  720.                         else
  721.                             begin
  722.                                 if v.h < 0 then
  723.                                     if him^.speed.h < 0 then
  724.                                         him^.speed.h := -him^.speed.h;
  725.                                 him^.position.h := him^.position.h + 1;
  726.                             end;
  727.                         if v.v > 0 then
  728.                             begin
  729.                                 if him^.speed.v > 0 then
  730.                                     him^.speed.v := -him^.speed.v;
  731.                                 him^.position.v := him^.position.v - 1;
  732.                             end
  733.                         else
  734.                             begin
  735.                                 if v.v < 0 then
  736.                                     if him^.speed.v < 0 then
  737.                                         him^.speed.v := -him^.speed.v;
  738.                                 him^.position.v := him^.position.v + 1;
  739.                             end;
  740.  
  741.                     end
  742.                 else
  743.                     begin
  744. {This looks like inside! Let's eat him.}
  745.                         score := score + 1;
  746.                         RedrawScoreFace;
  747.                         him^.task := nil;
  748.                         setCount := setCount - 1;
  749.                         if setCount < 2 then
  750.                             CreatePill; {There should always be pills left!}
  751.                         SATSoundPlay(bippH, 1, true);
  752.                     end; {Dist2}
  753.             end; {RegionHitTest}
  754.  
  755.     end;
  756.  
  757. {Create the score face}
  758.     procedure InitScoreFace;
  759.         var
  760.             r: Rect;
  761.     begin
  762.         SetRect(r, 0, 0, 80, 14);{}
  763.         scoreFace := SATNewFace(r);
  764.         SATChangedFace(scoreFace);
  765.         SetRect(r, 0, 0, 200, 16);{}
  766.         highFace := SATNewFace(r);
  767.         SATChangedFace(highFace);
  768.         SetRect(r, 0, 0, 120, 16);{}
  769.         lastFace := SATNewFace(r);
  770.         SATChangedFace(lastFace);
  771.     end;
  772.  
  773.     procedure SetupDummy (me: SpritePtr);
  774.     begin
  775.         me^.task := @SATBounce;
  776.     end;
  777.  
  778.     procedure SetupSmall (me: C3SpritePtr);
  779.     begin
  780.         me^.face := myFace;
  781.         me^.hotRect := me^.face^.iconMask.bounds;
  782.         me^.task := @HandleTheSprite;
  783.  
  784.         me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  785.         me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  786.     end;
  787.  
  788.     procedure SetupPlayer (me: C3SpritePtr);
  789.     begin
  790.         me^.face := playerFace[0];
  791.         me^.hotRect := me^.face^.iconMask.bounds;
  792.         me^.task := @HandlePlayer;
  793.         me^.hitTask := @HitPlayer;
  794.  
  795.         me^.fixedPos.h := BSL(me^.position.h, 4); {*16}
  796.         me^.fixedPos.v := BSL(me^.position.v, 4); {*16}
  797.     end;
  798.  
  799.  
  800.     procedure CreatePill;
  801.         var
  802.             sp: SpritePtr;
  803.     begin
  804.         sp := SATNewSprite(-1, SATRand(gSAT.offSizeH - 32), SATRand(gSAT.offSizeV - 32), @SetupSmall);
  805.         setCount := setCount + 1; {Number of active pills}
  806.     end;
  807.  
  808.  
  809.     procedure NewSet;
  810.         var
  811.             sp: SpritePtr;
  812.             i: integer;
  813.     begin
  814. {Kill all sprites}
  815.         while gSAT.sRoot <> nil do
  816.             SATKillSprite(gSAT.sRoot);
  817.  
  818. {Create the pills}
  819.         for i := 1 to 10 do
  820.             CreatePill;
  821.         if settings^^.high > 7 then
  822.             for i := 8 to settings^^.high do
  823.                 CreatePill;
  824.  
  825.         sp := SATNewSprite(0, SATRand(gSAT.offSizeH - 32), SATRand(gSAT.offSizeV - 32), @SetupDummy);
  826.         RedrawScoreFace;
  827.         sp^.face := scoreFace;
  828.         repeat
  829.             sp^.speed.h := SATRand(5) - 2
  830.         until sp^.speed.h <> 0;
  831.         repeat
  832.             sp^.speed.v := SATRand(3) - 1
  833.         until sp^.speed.v <> 0;
  834.         sp^.hotRect := sp^.face^.iconMask.bounds;
  835. {Hoppsan- fattas nåt!}
  836.  
  837.         sp := SATNewSprite(1, (gSAT.offSizeH - 32) div 2, (gSAT.offSizeV - 32) div 2, @SetupPlayer);
  838.         gMode := 0;
  839.  
  840.         SATBackChanged(gSAT.bounds);
  841.         FlushEvents(6, 0); {Glöm klick från förra uppsättningen!}
  842.         if not (TickCount > gameStartTime + kGameTime) then {Om tiden INTE är ute så skall vi ändra!}
  843.             lastSetStartTime := TickCount;
  844.     end;
  845.  
  846. {An example of how you can (with some effort) scale a sprite.}
  847.     procedure ScaleWelcomeFace;
  848.         var
  849.             srcFacePort, destFacePort: GrafPtr;
  850.             i: integer;
  851.             scaleRect: Rect;
  852.     begin
  853. {Get the rectangle of the original}
  854.         scaleRect := welcomeFace^.iconMask.bounds;
  855.  
  856.         for i := 0 to 31 do
  857.             begin
  858. {SetPortFace to the source. This must be done each turn since ChangedFace changes it.}
  859.                 SATSetPortFace(welcomeFace); {Set the FIRST of SAT's two internal face-ports to the original face.}
  860.                 GetPort(srcFacePort); {Get the port.}
  861. {Modify the size}
  862.                 scaleRect.bottom := scaleRect.bottom - 2;
  863.                 scaleRect.right := scaleRect.right - 2;
  864. {Create the new face}
  865.                 if scaledFace[i] = nil then
  866.                     scaledFace[i] := SATNewFace(scaleRect);
  867. {Get a port to it}
  868.                 SATSetPortFace2(scaledFace[i]); {Set the SECOND of SAT's two internal face ports to the new face.}
  869.                 GetPort(destFacePort); {Get the port.}
  870. {Copy the image}
  871.                 CopyBits(srcFacePort^.portBits, destFacePort^.portBits, welcomeFace^.iconMask.bounds, scaleRect, srcCopy + ditherCopy, nil);
  872.                 CopyBits(welcomeFace^.iconMask, scaledFace[i]^.iconMask, welcomeFace^.iconMask.bounds, scaleRect, srcCopy, nil);
  873.                 SATChangedFace(scaledFace[i]); {Done changing it. Tell SAT that it may do whatever it needs.}
  874.             end; {for}
  875.     end; {ScaleWelcomeFace}
  876.  
  877.     procedure WindUpdate (whatever: Boolean);
  878.         var
  879.             savePort: GrafPtr;
  880.             saveDev: GDHandle;
  881.     begin
  882.         if SATDepthChangeTest then
  883. {IMPORTANT! We must redraw all internally generated faces on depth changes!}
  884.             begin
  885.                 ReDrawPlayerFaces;
  886.                 RedrawScoreFace;
  887.                 RedrawHighFace;
  888.                 RedrawLastFace;
  889.                 ScaleWelcomeFace;
  890.  
  891. {We also have to redraw the background, since it's not a PICT (in which case that is automatic)}
  892.                 GetPort(savePort);
  893.                 if gSAT.colorFlag then
  894.                     saveDev := GetGDevice;
  895.                 SATSetPortBackScreen;
  896.                 SATPenPat(bgPat);
  897.                 PaintRect(gSAT.backScreen.port^.portRect);
  898.                 PenNormal;
  899.                 CopyBits(gSAT.backScreen.port^.portBits, gSAT.offScreen.port^.portBits, gSAT.offScreen.port^.portRect, gSAT.offScreen.port^.portRect, srcCopy, nil);
  900.                 SetPort(savePort);
  901.                 if gSAT.colorFlag then
  902.                     SetGDevice(saveDev);
  903.  
  904.             end;
  905.         SATRedraw;
  906.     end;
  907.  
  908.     procedure WindClose;
  909.     begin
  910.         SkelWhoa;
  911.     end;
  912.  
  913.     procedure WindMouse (where: Point; when: Longint; modifiers: integer);
  914.         var
  915.             found, sp: SpritePtr;
  916.             anyLeft: Boolean;
  917.             myRegion: RgnHandle;
  918.     begin
  919. {Not needed for the game, but note that we can check the mask region of a sprite}
  920. {towards a mouse click as well as a colliding sprite! For demonstrating this, mouse}
  921. {clicks are processed, and if a sprite is hit, a SysBeep is made. Try this by clicking}
  922. {in and around the "Hello" sprite!}
  923.  
  924.         myRegion := NewRgn;
  925.         sp := gSAT.sRoot;
  926.         found := nil;
  927.         while sp <> nil do                                        {Search through the sprite list}
  928.             begin
  929.                 if PtInRect(where, sp^.r) then                        {We are in the rect!}
  930.                     if sp^.face <> nil then                            {Does it have a face at all? Remember it's legal not to have one!}
  931.                         if sp^.face^.maskRgn <> nil then                {Does that face have a mask region? It should, but…}
  932.                             begin
  933.                                 CopyRgn(sp^.face^.maskRgn, myRegion);    {Copy the mask region}
  934.                                 OffsetRgn(myRegion, sp^.position.h, sp^.position.v);        {Offset it to the position of the sprite}
  935.                                 if PtInRgn(where, myRegion) then            {Are we in the region?}
  936.                                     found := sp;                                    {Yes!}
  937.                             end;
  938.                 sp := sp^.next;                                        {Next sprite…}
  939.             end;
  940.         if found <> nil then
  941.             SysBeep(1);                                            {We hit something. Tell us so.}
  942.         DisposeRgn(myRegion);
  943.     end;
  944.  
  945.  
  946.     procedure WindKey (theKey: char; theMods: integer);
  947.     begin
  948. {Hard-coded keys; real games have customizable keys.}
  949.         case theKey of
  950.             ',', 'z', '1': 
  951.                 gMode := BitAnd(gMode - 1, 15);
  952.             '.', 'x', '2': 
  953.                 gMode := BitAnd(gMode + 1, 15);
  954.             otherwise
  955.         end; {case}
  956.         ObscureCursor; {Hide the cursor until the mouse is moved.}
  957.     end;
  958.  
  959.     procedure SetupSAT (theWind: WindowPtr);{Calls SATCustomInit and paints the background with a pattern}
  960.         var
  961.             savePort: SATPort;
  962.             r: Rect;
  963.     begin
  964.         SATGetPort(savePort);
  965.  
  966.         SetPort(theWind);
  967.         r := theWind^.portRect;
  968.         OffsetRect(r, -r.left, -r.top);
  969.         SATCustomInit(0, 0, r, theWind, nil, false, false, false, true, false); {Nytt försök!}
  970. {SATCustomInit(0, 0, theWind^.portRect, theWind, nil, false, false, false, true, false); {Nytt försök!}
  971.  
  972. {We use a customized sprite record! Thus, we must SetSpriteSize before creating sprites!}
  973.         SATSetSpriteRecSize(sizeof(C3Sprite));
  974.  
  975.         if bgPat = nil then
  976.             bgPat := SATGetPat(128);
  977.         if bgPat = nil then
  978.             Barf;
  979.  
  980.         SATSetPortBackScreen;
  981.         SATPenPat(bgPat);
  982.         PaintRect(gSAT.backScreen.port^.portRect);
  983.         PenNormal;
  984.         CopyBits(gSAT.backScreen.port^.portBits, gSAT.offScreen.port^.portBits, gSAT.offScreen.port^.portRect, gSAT.offScreen.port^.portRect, srcCopy, nil);
  985.  
  986.         SATSetPort(savePort);
  987.         CopyBits(gSAT.backScreen.port^.portBits, gSAT.wind.port^.portBits, gSAT.wind.port^.portRect, gSAT.wind.port^.portRect, srcCopy, nil);
  988.  
  989.         if SkelWindow(theWind, @WindMouse, @WindKey, @WindUpdate, nil, @WindClose, nil, nil, false) then
  990.             ;
  991.  
  992.     end;
  993.  
  994.     procedure SetupWindow;
  995.         var
  996.             slaskWind, theWind: WindowPtr;
  997.             tmpWorld: SysEnvRec;
  998.             tmpCol: Boolean;
  999. {r: Rect;}
  1000. {peek: WindowPeek;}
  1001.     begin
  1002.         tmpCol := false;
  1003.         if noErr = SysEnvirons(1, tmpWorld) then
  1004.             tmpCol := tmpWorld.hasColorQD;
  1005.  
  1006.         if tmpCol then
  1007.             theWind := GetNewCWindow(theWindRes, nil, WindowPtr(-1))
  1008.         else
  1009.             theWind := GetNewWindow(theWindRes, nil, WindowPtr(-1));
  1010.  
  1011. {peek := WindowPeek(theWind);}
  1012.  
  1013.         if theWind = nil then
  1014.             Barf;
  1015.  
  1016. {MoveWindow(theWind, 50, 50, false);}
  1017.  
  1018. {r := WindowPeek(theWind)^.contRgn^^.rgnBBox;}
  1019.  
  1020. {slaskWind := theWind;}
  1021.  
  1022.         SetupSAT(theWind); {Calls SATCustomInit and paints the background with a pattern}
  1023.  
  1024.         SATSetPortScreen;
  1025.         ShowWindow(gSAT.wind.port);
  1026.         SelectWindow(gSAT.wind.port);
  1027.         SATRedraw;
  1028.     end;
  1029.  
  1030. {The task the welcome sprite has while zooming.}
  1031.     procedure ZoomWelcome (me: SpritePtr);
  1032.     begin
  1033.         me^.mode := me^.mode + 1;
  1034. {Compensate for the size change to make it centered in one place.}
  1035.         me^.position.h := me^.position.h - 1;
  1036.         me^.position.v := me^.position.v - 1;
  1037.         if me^.mode >= 32 then
  1038.             begin
  1039.                 me^.face := welcomeFace;
  1040.                 me^.task := @SATBounce;
  1041.             end
  1042.         else
  1043.             me^.face := scaledFace[32 - me^.mode];
  1044.     end;
  1045.  
  1046. {Initialize faces.}
  1047.     procedure InitSpriteFaces;
  1048.         var
  1049.             i: integer;
  1050.     begin
  1051.         myFace := SATGetFace(128);
  1052.         if myFace = nil then
  1053.             Barf;
  1054.         welcomeFace := SATGetFace(138);
  1055.         if welcomeFace = nil then
  1056.             Barf;
  1057. {We don't HAVE to bail out when a face fails to load - the program will stll wor, but that face will}
  1058. {not be visible.}
  1059.         ScaleWelcomeFace;
  1060.     end;
  1061.  
  1062.     var
  1063.         lastTime: Longint;
  1064.  
  1065. {DirtyWork is called from TransSkel}
  1066.     procedure DirtyWork;
  1067.         var
  1068.             sp: SpritePtr;
  1069.             ph: PicHandle;
  1070.             r: Rect;
  1071.             savePort: GrafPtr;
  1072.             saveDev: GDHandle;
  1073.     begin
  1074. {We can check TickCount as usual, since we never know how often we get null events.}
  1075.         if lastTime + 1 < TickCount then
  1076.             begin
  1077.                 SATRun(false);
  1078.                 lastTime := tickCount;
  1079.             end;
  1080.  
  1081.         if gameRunning then
  1082.             begin
  1083. {Timebar}
  1084.                 GetPort(savePort);
  1085.                 if gSAT.colorFlag then
  1086.                     saveDev := GetGDevice;
  1087.                 SATSetPortBackScreen;
  1088. {I *should* change only the part that actually changes!}
  1089.                 r := gSAT.wind.port^.portRect;
  1090.                 SATBackChanged(r);
  1091.                 r.right := 5;
  1092.                 r.top := r.bottom * (lastSetStartTime + kGameTime - TickCount) div kGameTime;
  1093.                 ForeColor(redColor); {Quickest way to get a color.}
  1094.                 PaintRect(r);
  1095.                 r.bottom := r.top;
  1096.                 r.top := 0;
  1097.                 SATPenPat(bgPat);
  1098.                 PaintRect(r);
  1099.                 PenNormal;
  1100.  
  1101.                 SetPort(savePort);
  1102.                 if gSAT.colorFlag then
  1103.                     SetGDevice(saveDev);
  1104. {end of Timebar}
  1105.  
  1106.                 if TickCount > lastSetStartTime + kGameTime then
  1107.                     begin
  1108.                         SATSoundPlay(baeH, 5, true);
  1109. {NewSet;}
  1110.  
  1111.                         if TickCount > gameStartTime + kGameTime then
  1112.                             begin
  1113.                                 if score > settings^^.high then
  1114.                                     begin
  1115. {settings^^.high := score;}
  1116.                                         SATSoundEvents;
  1117.                                         AskHigh;
  1118.                                         ChangedResource(Handle(settings));
  1119.                                     end;
  1120.  
  1121. {Kill all sprites}
  1122.                                 while gSAT.sRoot <> nil do
  1123.                                     SATKillSprite(gSAT.sRoot);
  1124.  
  1125.                                 RedrawHighFace;
  1126.                                 RedrawLastFace;
  1127. {Time for breaking some of my conventions! The stuff below should be done in "setup" and "handle"}
  1128. {routines, as recommened in the manual and done in other demos - but if we want to mess up the code,}
  1129. {we are free to do so! The sprites below set up their faces and speeds right here, and share a common}
  1130. {handling routine (SATBounce).}
  1131.  
  1132. {Make the "hello" sprite}
  1133.                                 sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 2, @SetupDummy);
  1134.                                 sp^.face := welcomeFace;
  1135.                                 repeat
  1136.                                     sp^.speed.h := SATRand(3) - 1
  1137.                                 until sp^.speed.h <> 0;
  1138.                                 repeat
  1139.                                     sp^.speed.v := SATRand(3) - 1
  1140.                                 until sp^.speed.v <> 0;
  1141.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1142.                                 sp^.task := @ZoomWelcome;
  1143. {High score sprite}
  1144.                                 sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 4, @SetupDummy);
  1145.                                 sp^.face := highFace;
  1146.                                 repeat
  1147.                                     sp^.speed.h := SATRand(7) - 3
  1148.                                 until sp^.speed.h <> 0;
  1149.                                 repeat
  1150.                                     sp^.speed.v := SATRand(3) - 1
  1151.                                 until sp^.speed.v <> 0;
  1152.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1153. {Last score sprite}
  1154.                                 sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 3, @SetupDummy);
  1155.                                 sp^.face := lastFace;
  1156.                                 repeat
  1157.                                     sp^.speed.h := SATRand(7) - 3
  1158.                                 until sp^.speed.h <> 0;
  1159.                                 repeat
  1160.                                     sp^.speed.v := SATRand(3) - 1
  1161.                                 until sp^.speed.v <> 0;
  1162.                                 sp^.hotRect := sp^.face^.iconMask.bounds;
  1163.  
  1164.                                 SATSetPortScreen;
  1165.                                 SATRedraw; {Just to make sure killed sprites are erased}
  1166.                                 gameRunning := false;
  1167.                             end;
  1168.  
  1169.                     end;
  1170.             end;
  1171.  
  1172.         if not gameRunning then
  1173.             if gSAT.sRoot = nil then
  1174.                 begin
  1175. {Messy code for setting up the "hello" sprite - which is why I recommend the use of setup routines.}
  1176.                     sp := SATNewSprite(0, gSAT.offSizeH div 2, gSAT.offSizeV div 2, @SetupDummy);
  1177.                     sp^.face := welcomeFace;
  1178.                     repeat
  1179.                         sp^.speed.h := SATRand(3) - 1
  1180.                     until sp^.speed.h <> 0;
  1181.                     repeat
  1182.                         sp^.speed.v := SATRand(3) - 1
  1183.                     until sp^.speed.v <> 0;
  1184.                     sp^.hotRect := sp^.face^.iconMask.bounds;
  1185.                     sp^.task := @ZoomWelcome;
  1186.                 end;
  1187.     end;
  1188.  
  1189.     procedure InitHigh;
  1190.     begin
  1191.         settings := SettingsHnd(GetResource('Sett', 0));
  1192.         if settings = nil then {Didn't exist - create it!}
  1193.             begin
  1194.                 settings := SettingsHnd(NewHandle(Sizeof(SettingsRec)));
  1195.                 if settings = nil then
  1196.                     begin
  1197.                         SysBeep(1);
  1198.                         halt;
  1199.                     end;
  1200.                 settings^^.high := 0;
  1201.                 AddResource(handle(settings), 'Sett', 0, '');
  1202.             end
  1203.         else {Did exist - check the size!}
  1204.             if GetHandleSize(Handle(settings)) < sizeof(SettingsRec) then
  1205.                 SetHandleSize(Handle(settings), sizeof(SettingsRec));
  1206.     end;
  1207.  
  1208.     procedure DoFileMenu (item: integer);
  1209.     begin
  1210.         case item of
  1211.             newGameItem: 
  1212.                 begin
  1213.                     score := 0;
  1214.                     gameRunning := true;
  1215.                     gameStartTime := TickCount;
  1216.                     lastSetStartTime := TickCount;
  1217.                     setCount := 0;
  1218.                     NewSet;
  1219.  
  1220.                     ObscureCursor; {Hide the cursor until the mouse is moved.}
  1221.                 end;
  1222.             clearHighItem: 
  1223.                 if SATQuestionStr('Set the high score to zero?') then
  1224.                     begin
  1225.                         settings^^.high := 0;
  1226.                         ChangedResource(handle(settings));
  1227.                     end;
  1228.             otherwise
  1229.                 SkelWhoa;
  1230.         end;
  1231.     end;
  1232.  
  1233.     procedure DoShapeMenu (item: integer);
  1234.         const
  1235.             wide = 1;
  1236.             tall = 2;
  1237.         var
  1238.             p: Point;
  1239.     begin
  1240.         p := gSAT.wind.port^.portRect.botRight;
  1241.         case item of
  1242.             wide: 
  1243.                 if gSAT.wind.port^.portRect.bottom > gSAT.wind.port^.portRect.right then
  1244.                     begin
  1245.                         CheckItem(shapeMenu, wide, true);
  1246.                         CheckItem(shapeMenu, tall, false);
  1247.                         SizeWindow(gSAT.wind.port, p.v, p.h, false); {swap size}
  1248.                         SATKill;
  1249.                         SetupSAT(gSAT.wind.port);
  1250.                         gameRunning := false;
  1251.                     end;
  1252.             tall: 
  1253.                 if gSAT.wind.port^.portRect.bottom < gSAT.wind.port^.portRect.right then
  1254.                     begin
  1255.                         CheckItem(shapeMenu, tall, true);
  1256.                         CheckItem(shapeMenu, wide, false);
  1257.                         SizeWindow(gSAT.wind.port, p.v, p.h, false); {swap size}
  1258.                         SATKill;
  1259.                         SetupSAT(gSAT.wind.port);
  1260.                         gameRunning := false;
  1261.                     end;
  1262.             otherwise
  1263.                 SysBeep(1);
  1264.         end;{case}
  1265.     end;
  1266.  
  1267.     procedure SetUpMenus;
  1268.     begin
  1269.         SkelApple('About CollisionIII…', @DoAbout);
  1270.         fileMenu := GetMenu(fileMenuRes);
  1271.         if fileMenu = nil then
  1272.             Barf;
  1273.         if SkelMenu(fileMenu, @DoFileMenu, nil, true) then
  1274.             ;
  1275.         shapeMenu := GetMenu(shapeMenuRes);
  1276.         if shapeMenu = nil then
  1277.             Barf;
  1278.         if SkelHMenu(shapeMenu, @DoShapeMenu, nil) then {Install as hierarcical menu}
  1279.             ;
  1280.         CheckItem(shapeMenu, 1, true); {Check "wide"}
  1281.     end;
  1282.  
  1283. begin
  1284.     SkelInit(6, nil);
  1285.     SkelSetSleep(0); {Tell TransSkel that we want attention as often as possible.}
  1286.     SetupMenus;
  1287.     SetupWindow;
  1288.  
  1289.     InitHigh;
  1290.     InitSpriteFaces;
  1291.     InitScoreFace;
  1292.     InitPlayerFaces;
  1293.  
  1294.     SkelBackground(@DirtyWork);
  1295.     lastTime := TickCount;
  1296. {$IFC UNDEFINED THINK_PASCAL}
  1297.     qd.randSeed := TickCount;
  1298. {$ELSEC}
  1299.     randSeed := TickCount;
  1300. {$ENDC}
  1301.  
  1302.     Synth; {Build sounds!}
  1303.     SATSoundShutup;
  1304.  
  1305.     SkelMain;
  1306.     SkelClobber;
  1307.     SATSoundShutup;
  1308. end.